
open Ast
open Ast_fullfill
open Ast_absolute

module StrSet = Set.Make(String)

let effectable_list func (data:StrSet.t) (declared:StrSet.t) lst = 
	List.fold_left (fun data e -> func data declared e) data lst

let rec effectable_sourceElement data declared = function
	| AStatement(s) -> effectable_statement data declared s
	(*| AFunctionDeclaration(funcDecl) -> effectable_functionDeclaration data declared funcDecl *) (* Modif #1 *)

and effectable_statement data declared = function
	| ABlock(block) -> effectable_block data declared block
	| AVariableStatement(varStmt) -> effectable_variableStatement data declared varStmt
	| AConstStatement(varStmt) -> effectable_variableStatement data declared varStmt		(* Modif #2 *)
	| AFuncDeclaration(funcDecl) -> effectable_functionDeclaration data declared funcDecl	(* Modif #1 *)
	| AEmptyStatement -> data
	| AExpressionStatement(expressionStatement) -> effectable_expressionStatement data declared expressionStatement
	| AIfStatement(ifStatement) -> effectable_ifStatement data declared ifStatement
	| AIterationStatement(iterationStatement) -> effectable_iterationStatement data declared iterationStatement
	| AContinueStatement _ -> data
	| ABreakStatement _ -> data
	| AReturnStatement(returnStatement) -> effectable_returnStatement data declared returnStatement
	| AWithStatement(withStatement) -> effectable_withStatement data declared withStatement
	| ALabelledStatement(labelledStatement) -> effectable_labelledStatement data declared labelledStatement
	| ASwitchStatement(switchStatement) -> effectable_switchStatement data declared switchStatement
	| AThrowStatement(throwStatement) -> effectable_throwStatement data declared throwStatement
	| ATryStatement(tryStatement) -> effectable_tryStatement data declared tryStatement
	| ADebuggerStatement -> data
	
and effectable_block data declared block =
	effectable_list effectable_statement data declared block

and effectable_variableStatement data declared (_,_,stmt) =
	effectable_variableDeclarationList data declared stmt

and effectable_variableDeclarationList data declared decls =
	effectable_list effectable_variableDeclaration data declared decls;

and effectable_variableDeclaration data declared (ident,init) = match init with
	| None -> data
	| Some init -> effectable_initializer data declared init

and effectable_initializer data declared assignExpr =
	effectable_assignmentExpression data declared assignExpr

and effectable_assignmentExpression data declared = function
	| ACondExpr(condExpr) -> effectable_conditionalExpression data declared condExpr
	| AAssignOp(lhs, op, assignExpr) ->
		let data = effectable_leftHandSideExpression data declared lhs in
		effectable_assignmentExpression data declared assignExpr

and effectable_leftHandSideExpression data declared = function
	| ANewExpression(expr) -> effectable_newExpression data declared expr
	| ACallExpression(expr) -> effectable_callExpression data declared expr

and effectable_newExpression data declared = function
	| AMemberExpression(expr) -> effectable_memberExpression data declared expr
	| ANewMemberExpression(expr) -> effectable_newExpression data declared expr

and effectable_callExpression data declared = function
	| ACallMemberArgs(expr, args) ->
		let data = effectable_memberExpression data declared expr in
		effectable_arguments data declared args
	| ACallExpressionArgs(expr, args) ->
		let data = effectable_callExpression data declared expr in
		effectable_arguments data declared args
	| ACallExpressionExpr(expr1, expr2) -> 
		let data = effectable_callExpression data declared expr1 in
		effectable_expression data declared expr2
	| ACallExpressionIdent(expr, ident) ->
		effectable_callExpression data declared expr

and effectable_memberExpression data declared = function
	| APrimaryExpr(expr) -> effectable_primaryExpression data declared expr
	| AFunctionExpr(funcExpr) -> effectable_functionExpression data declared funcExpr
	| AMemberBracketExpr(expr1, expr2) -> 
		let data = effectable_memberExpression data declared expr1 in
		effectable_expression data declared expr2
	| AMemberDotExpr(expr, ident) ->
		effectable_memberExpression data declared expr
	| AMemberNewExpr(expr, args) ->
		let data = effectable_memberExpression data declared expr in
		effectable_arguments data declared args

and effectable_functionDeclaration data declared (_, _, _, _, params, body) =
	effectable_functionContent data declared params body

and effectable_functionExpression data declared (_, _, params, body) = 
	effectable_functionContent data declared params body

and effectable_functionContent data declared params body =
	let declared = StrSet.union declared (let fullfill = fullfill_functionContent params body in fullfill.idents) in
	let absolute = absolute_list absolute_sourceElement {
			abs_labels = StrSet.empty;
			abs_idents = StrSet.empty
		} declared body in
	StrSet.union data absolute.abs_idents

and effectable_arguments data declared args =
	effectable_list effectable_assignmentExpression data declared args

and effectable_expressionStatement data declared expr =
	effectable_expression data declared expr

and effectable_ifStatement data declared = function
	| AIfElse(expr, stmt1, stmt2) ->
		let data = effectable_expression data declared expr in
		let data = effectable_statement data declared stmt1 in
		effectable_statement data declared stmt2
	| AIf(expr, stmt) ->
		let data = effectable_expression data declared expr in
		effectable_statement data declared stmt

and effectable_iterationStatement data declared = function
	| ADoWhile(stmt, expr) ->
		let data = effectable_statement data declared stmt in
		effectable_expression data declared expr
	| AWhile(expr, stmt) ->
		let data = effectable_expression data declared expr in
		effectable_statement data declared stmt
	| AFor(expr1, expr2, expr3, stmt) ->
		let data = (match expr1 with
			| None -> data
			| Some expr1 -> effectable_expression data declared expr1) in
		let data = (match expr2 with
			| None -> data
			| Some expr2 -> effectable_expression data declared expr2) in
		let data = (match expr3 with
			| None -> data
			| Some expr3 -> effectable_expression data declared expr3) in
		effectable_statement data declared stmt
	| AForIn(lhs, expr, stmt) ->
		let data = effectable_leftHandSideExpression data declared lhs in
		let data = effectable_expression data declared expr in
		effectable_statement data declared stmt
	| AForVar(_, varDecls, expr1, expr2, stmt) ->
		let data = effectable_variableDeclarationList data declared varDecls in
		let data = (match expr1 with
			| None -> data
			| Some expr1 -> effectable_expression data declared expr1) in
		let data = (match expr2 with
			| None -> data
			| Some expr2 -> effectable_expression data declared expr2) in
		effectable_statement data declared stmt
	| AForVarIn(_, varDecl, expr, stmt) ->
		let data = effectable_variableDeclaration data declared varDecl in
		let data = effectable_expression data declared expr in
		effectable_statement data declared stmt
	
and effectable_returnStatement data declared = function
	| None -> data
	| Some expr -> effectable_expression data declared expr

and effectable_withStatement data declared (expr, stmt) = 
	let data = effectable_expression data declared expr in
	effectable_statement data StrSet.empty stmt	(* Important : with() {} makes as if everything was overriden *)

and effectable_labelledStatement data declared (ident, stmt) =
	effectable_statement data declared stmt

and effectable_switchStatement data declared (expr, caseBlock) = 
	let data = effectable_expression data declared expr in
	effectable_caseBlock data declared caseBlock

and effectable_caseBlock data declared = function
	| ACasesNoDefault(caseClauses) -> 
		effectable_caseClauses data declared caseClauses
	| ACasesDefault(caseClauses1, defaultClause, caseClauses2) ->
		let data = effectable_caseClauses data declared caseClauses1 in
		let data = effectable_defaultClause data declared defaultClause in
		effectable_caseClauses data declared caseClauses2

and effectable_defaultClause data declared stmts =
	effectable_list effectable_statement data declared stmts

and effectable_caseClauses data declared clauses = 
	effectable_list effectable_caseClause data declared clauses;
	
and effectable_caseClause data declared (expr,stmts) =
	let data = effectable_expression data declared expr in
	effectable_list effectable_statement data declared stmts

and effectable_throwStatement data declared expr =
	effectable_expression data declared expr

and effectable_tryStatement data declared = function
	| ATryCatch(block, catch) ->
		let data = effectable_block data declared block in
		effectable_catch data declared catch
	| ATryFinally(block, finally) ->
		let data = effectable_block data declared block in
		effectable_finally data declared finally
	| ATryCatchFinally(block, catch, finally) ->
		let data = effectable_block data declared block in
		let data = effectable_catch data declared catch in
		effectable_finally data declared finally

and effectable_catch data declared (_,block) =
	effectable_block data declared block

and effectable_finally data declared block =
	effectable_block data declared block

and effectable_conditionalExpression data declared = function
	| ACondBinaryExpr(expr) -> effectable_binaryExpression data declared expr
	| ACondTernaryExpr(expr, assignExpr1, assignExpr2) ->
		let data = effectable_binaryExpression data declared expr in
		let data = effectable_assignmentExpression data declared assignExpr1 in
		effectable_assignmentExpression data declared assignExpr2

and effectable_binaryExpression data declared = function
	| AUnaryExpr(unaryExpression) -> effectable_unaryExpression data declared unaryExpression
	| ABinaryExpr(_, expr1, expr2) ->
		let data = effectable_binaryExpression data declared expr1 in
		effectable_binaryExpression data declared expr2

and effectable_unaryExpression data declared = function
	| APostfixExpr(expr) -> effectable_postfixExpression data declared expr
	| AOpUnaryExpr(_, expr) -> effectable_unaryExpression data declared expr

and effectable_postfixExpression data declared = function
	| ALeftHandSide(lhs) -> effectable_leftHandSideExpression data declared lhs
	| ALeftHandSideOp(lhs, _) -> effectable_leftHandSideExpression data declared lhs

and effectable_expression data declared expr =
	effectable_list effectable_assignmentExpression data declared expr

and effectable_primaryExpression data declared = function
	| AThis -> data
	| AIdentifier(identifier) -> data
	| ALiteral(literal) -> data
	| AArrayLiteral(arrayLiteral) -> effectable_arrayLiteral data declared arrayLiteral
	| AObjectLiteral(objectLiteral) -> effectable_objectLiteral data declared objectLiteral
	| AExpression(expr) -> effectable_expression data declared expr

and effectable_arrayLiteral data declared = function
	| AElisions(elisions) -> data
	| AElements(elements) -> effectable_elements data declared elements
	| AElementsAndElisions(elements, _) -> effectable_elements data declared elements

and effectable_elements data declared elements =
	effectable_list effectable_element data declared elements

and effectable_element data declared (_, expr) =
	effectable_assignmentExpression data declared expr

and effectable_objectLiteral data declared objectLiteral =
	effectable_list effectable_propertyAssignment data declared objectLiteral

and effectable_propertyAssignment data declared = function
	| APropAssignment(_, assignExpr) -> 
		effectable_assignmentExpression data declared assignExpr
	| APropGet(_, functionBody) ->
		effectable_functionContent data declared [] functionBody
	| APropSet(_, idents, functionBody) ->
		effectable_functionContent data declared idents functionBody

